#ifndef SPRINGAINTERFACE_H_
#define SPRINGAINTERFACE_H_

#include "../rts/ExternalAI/IAICallback.h"
#include "../rts/ExternalAI/IGlobalAI.h"
#include "../rts/ExternalAI/IGlobalAICallback.h"


#include "../OOInterface/AIInterfaceImp.h"


//Boost includes
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/thread.hpp>


//STL includes
#include <set>
#include <queue>

/**
 * @VERSION Spring 0.77b5
 */

class SpringAIInterface : public OOInterface::AIInterfaceImp, public IGlobalAI
{
public:
	SpringAIInterface();
	virtual ~SpringAIInterface();


	/*************************IGlobalAI******************************************/
	/* The following are Methods specified by Spring's IGlobalAI interface.
	 *
	 * Note: For any int's that are used to designate a Unit, this integer
	 * can be assumed to be a unique identifier for the unit.
	 */

	/**
	 * InitAI( ... ) :
	 * This is the first method called by Spring when setting up the AI.
	 *
	 * @param callback The callback the AI should use to access game information
	 * @param team The team number of this AI.
	 */
	virtual void InitAI(IGlobalAICallback* callback, int team);

	/**
	 * UnitCreated( ... ) :
	 * Whenever a Unit is created by a building, builder or other means in Spring, this
	 * method is called. It is important to note that /created/ does not mean /finished/.
	 * Creation merely implies the Unit has begun to be built and a UnitFinished(...)
	 * will be called when the Unit is finished building. This is only called for Units
	 * that /this/ AI owns.
	 *
	 * @param unit The id of the unit created.
	 */
	virtual void UnitCreated( int unit );

	/**
	 * UnitFinished( ... ) :
	 * Whenever a Unit is finished building, this method is called by Spring. As noted in
	 * UnitCreated()'s description, UnitFinished() implies that the Unit is now available
	 * for use. This is only called for Units that /this/ AI owns.
	 *
	 * @param unit The id of the unit that finished building.
	 */
	virtual void UnitFinished( int unit );

	/**
	 * UnitDamaged( ... ) :
	 * Whenever a unit that this AI owns is damaged, this method is called by Spring. It is
	 * important to note that this is only called for Units this AI owns so other means must
	 * be used to notice damage done to units owned by the AI's teammates.
	 *
	 * @param damaged The Unit that was damaged. This unit is owned by this AI.
	 * @param attacker The Unit that attacked the damaged Unit. (not necessarily an enemy)
	 * @param damage The amount of damage dealt to the damaged unit.
	 * @param dir //TODO: WHAT IN THE WORLD IS DIR? The direction from which the attack came?
	 */
	virtual void UnitDamaged( int damaged, int attacker, float damage, float3 dir );

	/**
	 * UnitDestroyed( ... ) :
	 * Whenever a Unit is destroyed, this method is called by Spring. In addition, the
	 * attacker that dealt the killing blow is also included in the information about
	 * the unit's death. This is only called for destroyed Units that /this/ AI owns.
	 *
	 * @param unit The unit that was destroyed.
	 * @param attacker The unit that destroyed /unit/.
	 */
	virtual void UnitDestroyed( int unit, int attacker );

	/**
	 * UnitMoveFailed( ... )
	 * If the unit fails to reach the destination it was commanded to go to, this method is
	 * called.
	 *
	 * //TODO: The API notes this is for ground units only, but I suspect it is for any unit
	 * 			that was obstructed. (naval particularly)
	 *
	 * @param unit The unit that was unable to reach its destination.
	 */
	virtual void UnitMoveFailed( int unit );

	/**
	 * EnemyEnterLOS( ... ) :
	 * Whenever an enemy Unit enters this AI's LOS /or/ the AI's Team's LOS, Spring
	 * calls this method.
	 *
	 * @param enemy The enemy Unit that entered the LOS available to this AI.
	 * @see EnemyLeaveLOS( ... )
	 */
	virtual void EnemyEnterLOS( int enemy );

	/**
	 * EnemyLeaveLOS( ... ) :
	 * Whenever an enemy Unit leaves this AI's LOS /or/ the AI's Team's LOS, Spring
	 * calls this method. This method complements EnemyEnterLOS().
	 *
	 * @param enemy The enemy Unit that left the LOS available to this AI.
	 * @see EnemyEnterLOS( ... )
	 */
	virtual void EnemyLeaveLOS( int enemy );

	/**
	 * EnemyEnterRadar( ... ) :
	 * Whenever an enemy Unit enters this AI's radar /or/ the AI's Team's radar,
	 * Spring calls this method.
	 *
	 * @param enemy The enemy Unit that entered the radar available to this AI.
	 * @see EnemyLeaveRadar( ... )
	 */
	virtual void EnemyEnterRadar( int enemy );

	/**
	 * EnemyLeaveRadar( ... ) :
	 * Whenever an enemy Unit leaves this AI's radar /or/ the AI's Team's radar,
	 * Spring calls this method.
	 *
	 * @param enemy The enemy Unit that left the radar available to this AI.
	 * @see EnemyEnterRadar( ... )
	 */
	virtual void EnemyLeaveRadar( int enemy );

	/**
	 * EnemyDamaged( ... ) :
	 * Whenever an enemy Unit within LOS or Radar is damaged by /any/ source Unit
	 * this method is called.
	 *
	 * @param damaged The enemy Unit that took the damage within Radar/LOS.
	 * @param attacker The Unit that dealt the damage to the Unit. (can be friendly or hostile)
	 * @param damage The amount of damage dealth to the Unit by the attacker.
	 * @param dir  ???? //TODO: Figure out what dir is
	 */
	virtual void EnemyDamaged( int damaged, int attacker, float damage, float3 dir );

	/**
	 * EnemyDestroyed( ... ) :
	 * Whenever an enemy Unit within LOS or Radar is destroyed by /any/ source Unit
	 * this method is called.
	 *
	 * @param enemy The enemy Unit that was destroyed.
	 * @param attacker The Unit that destroyed the enemy Unit. (can be friendly or hostile)
	 */
	virtual void EnemyDestroyed( int enemy, int attacker );

	/**
	 * UnitIdle( ... ) :
	 * Whenever a Unit that this AI owns goes idle, this method is called. Idle is defined
	 * as any period where the Unit does not have any commands in its' command queue.
	 *
	 * @param unit The unit that is idling. This is a unique id.
	 */
	virtual void UnitIdle( int unit );

	/**
	 * GotChatMsg( ... ) :
	 * Whenever a message is sent in game by a Player, this method is called with the message
	 * and the sending player. This will -not- be called whenever this AI sends a message.
	 * Only messages other players send will be forwarded.
	 *
	 * @param msg The message that was sent by the player.
	 * @param player The player that sent the message. This is a unique ID.
	 */
	virtual void GotChatMsg( const char* msg, int player );

	/**
	 * HandleEvent( ... ) :
	 * This is used for general messaging between Spring and the AI. At the moment
	 * this is an unused feature.
	 *
	 * //TODO: While it says it is unused, this may be an outdated comment. Check into
	 * 		ChangeTeamEvent, WeaponFireEvent, PlayerCommandEvent, and SeismicPingEvent
	 *
	 * @param msg The id of the message so that the data can be interpretted. Magic Numbers
	 * @param data The data payload of the message.
	 */
	virtual int HandleEvent( int msg, const void* data );

	/*
	 * Update() :
	 * Called every frame by the Spring engine.
	 */
	virtual void Update();


	//Omitted Methods: Load and Save, they are not pure virtual, and thus not required.
	//TODO: Add save and load features if time permits (if save and load is actually used by Spring)

	/*************************IGlobalAI******************************************/
	/* End of the IGlobalAI interface requirements
	 */


	/*************************AIInterfaceImp*************************************/
	/* The following are methods specified by the AIInterfaceImp interface
	 * This concrete implementation of AIInterfaceImp is used as the implementation
	 * for the bridge calls by AIInterface (Bridge pattern GoF pg. 151)
	 *
	 * Refer to AIInterfaceImp.h for what the methods should do.
	 */

	virtual void attachAI( OOInterface::AIHandle* ai ) { _ai = ai; }
	virtual void sendTextMessage( const std::string& message, OOInterface::Player* player = NULL, bool team = false );
	virtual bool orderUnit( OOInterface::ConstUnitPtr unit, OOInterface::ConstCommandPtr command );
	virtual const OOInterface::GameMap& getGameMap();
	virtual const OOInterface::ConstPlayerSet& getPlayers();

	virtual const OOInterface::ResourceList& getResourceTypes();

	/*
	 * getCommandFactory() :
	 * Returns a CommandFactory that makes special Spring commands that
	 * convert the Command into the Spring version of Command.
	 *
	 * @return The requested CommandFactory
	 */
	virtual OOInterface::CommandFactory& getCommandFactory();


	/*************************AIInterfaceImp*************************************/
	/* End of the AIInterfaceImp interface requirements
	 */

private:
	IGlobalAICallback* _callback;

	OOInterface::AIHandle* _ai;

	boost::thread _aiThread;

	//Any time a unit is seen/created it is converted from its' Unit_Def form
	//to the Unit object form and stored for reuse.


	OOInterface::UnitSet _unitPrototypes;
	void createUnitPrototypes();

	OOInterface::UnitSet _units;

	OOInterface::GameMap* _gameMap;
	void initGameMap();

	OOInterface::TeamPtr _enemyTeam;
	OOInterface::TeamPtr _friendlyTeam;

	OOInterface::ConstPlayerSet _players;
	OOInterface::PlayerPtr _self;
	void initPlayers();
	void updatePlayers();
	void updateFriendlyUnits();
	void updateEnemyUnits();

	int* _idCache;

	//Resources Available in Spring Mods: Energy & Metal
	OOInterface::Resource _metal;
	OOInterface::Resource _energy;

	OOInterface::ResourceList _resources;


	// Information queueing for thread-safety
	// submitted when Spring calls update()
	void flushBuffers();
	void flushCommandBuffer();
	void flushTextBuffer();

	boost::mutex _cmdBufferMutex;
	boost::mutex _textBufferMutex;

	typedef std::pair< OOInterface::ConstUnitPtr, OOInterface::ConstCommandPtr > CommandUnitPair;
	std::queue< CommandUnitPair > _commandBuffer;

	std::queue< std::string > _textBuffer;




	inline void insertCommandBuffer( OOInterface::ConstUnitPtr unit, OOInterface::ConstCommandPtr cmd ) {
		boost::lock_guard< boost::mutex > lock( _cmdBufferMutex );
		_commandBuffer.push( CommandUnitPair (unit, cmd) );
	}

	inline void insertTextBuffer( std::string msg )
	{
		boost::lock_guard< boost::mutex > lock( _textBufferMutex );

		_textBuffer.push( msg );
	}



	// Convenience methods
	inline bool isInitialized() { return _callback; }
	inline IAICallback* getAICallback() { return isInitialized() ? _callback->GetAICallback() : NULL; }
	inline IAICheats* getAICheats() { return isInitialized() ? _callback->GetCheatInterface() : NULL; }



	/*
	 * This method should be used to translate unit_id's -> Unit.
	 * It will handle creation of new Units if it doesn't already exist
	 * or hand out previous created Units if it can find one with the
	 * same ID.
	 */
	OOInterface::UnitPtr getUnitByID( int id );

	OOInterface::UnitPtr createUnit( int id );

};


#endif /*SPRINGAINTERFACE_H_*/
